From 95b19bca33c9571861915be58e63d8dbe855d4e4 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 14 Mar 2011 11:46:43 +0100 Subject: [PATCH] broadway: Implement pointer grabs --- gdk/broadway/broadway.c | 42 +++++++++++ gdk/broadway/broadway.h | 6 ++ gdk/broadway/broadway.js | 110 ++++++++++++++++++++++++++++- gdk/broadway/gdkdevice-broadway.c | 84 +++++++++++++++++++++- gdk/broadway/gdkdisplay-broadway.c | 8 ++- gdk/broadway/gdkeventsource.c | 14 +++- gdk/broadway/gdkprivate-broadway.h | 3 +- 7 files changed, 256 insertions(+), 11 deletions(-) diff --git a/gdk/broadway/broadway.c b/gdk/broadway/broadway.c index b393879d2a..bc78de1e58 100644 --- a/gdk/broadway/broadway.c +++ b/gdk/broadway/broadway.c @@ -661,6 +661,48 @@ broadway_output_query_pointer (BroadwayOutput *output, int id) return serial; } +guint32 +broadway_output_grab_pointer (BroadwayOutput *output, + int id, + gboolean owner_event, + guint32 time_) +{ + char buf[HEADER_LEN + 3 + 1 + 6]; + guint32 serial; + int p; + + serial = output->serial; + p = write_header (output, buf, 'g'); + append_uint16 (id, buf, &p); + buf[p++] = owner_event ? '1': '0'; + append_uint32 (time_, buf, &p); + + assert (p == sizeof (buf)); + + broadway_output_write (output, buf, sizeof (buf)); + + return serial; +} + +guint32 +broadway_output_ungrab_pointer (BroadwayOutput *output, + guint32 time_) +{ + char buf[HEADER_LEN + 6]; + guint32 serial; + int p; + + serial = output->serial; + p = write_header (output, buf, 'u'); + append_uint32 (time_, buf, &p); + + assert (p == sizeof (buf)); + + broadway_output_write (output, buf, sizeof (buf)); + + return serial; +} + void broadway_output_new_surface(BroadwayOutput *output, int id, int x, int y, int w, int h) { diff --git a/gdk/broadway/broadway.h b/gdk/broadway/broadway.h index b0d3c2a61f..c750a2497b 100644 --- a/gdk/broadway/broadway.h +++ b/gdk/broadway/broadway.h @@ -57,3 +57,9 @@ void broadway_output_copy_rectangles (BroadwayOutput *output, int dy); guint32 broadway_output_query_pointer (BroadwayOutput *output, int id); +guint32 broadway_output_grab_pointer (BroadwayOutput *output, + int id, + gboolean owner_event, + guint32 time_); +guint32 broadway_output_ungrab_pointer (BroadwayOutput *output, + guint32 time_); diff --git a/gdk/broadway/broadway.js b/gdk/broadway/broadway.js index da4b711460..acdace7b02 100644 --- a/gdk/broadway/broadway.js +++ b/gdk/broadway/broadway.js @@ -76,9 +76,15 @@ function createXHR() return null; } +var grab = new Object(); +grab.window = null; +grab.owner_events = false; +grab.time = 0; +grab.implicit = false; var last_serial = 0; var last_x = 0; var last_y = 0; +var real_window_with_mouse = 0; var window_with_mouse = 0; var surfaces = {}; var outstanding_commands = new Array(); @@ -99,6 +105,14 @@ function initContext(canvas, x, y, id) return context; } +var GDK_GRAB_SUCCESS = 0; +var GDK_GRAB_ALREADY_GRABBED = 1; +var GDK_GRAB_INVALID_TIME = 2; + +var GDK_CROSSING_NORMAL = 0; +var GDK_CROSSING_GRAB = 1; +var GDK_CROSSING_UNGRAB = 2; + function handleCommands(cmd_obj) { var cmd = cmd_obj.data; @@ -265,6 +279,40 @@ function handleCommands(cmd_obj) send_input ("q", [pos.root_x, pos.root_y, pos.win_x, pos.win_x, window_with_mouse]); break; + case 'g': // Grab + var id = base64_16(cmd, i); + i = i + 3; + var owner_events = cmd[i++] == '1'; + var time = base64_32(cmd, i); + i = i + 6; + + if (grab.window != null) { + /* Previous grab, compare times */ + if (time != 0 && grab.time != 0 && + time > grab.time) { + send_input ("g", [GDK_GRAB_INVALID_TIME]); + break; + } + } + + doGrab(id, owner_events, time, false); + + send_input ("g", [GDK_GRAB_SUCCESS]); + + break; + + case 'u': // Ungrab + var time = base64_32(cmd, i); + i = i + 6; + send_input ("u", []); + + if (grab.window != null) { + if (grab.time == 0 || time == 0 || + grab.time < time) + grab.window = null; + } + + break; default: alert("Unknown op " + command); } @@ -349,41 +397,99 @@ function getPositionsFromEvent(ev, relativeId) { return res; } +function getEffectiveEventTarget (id) { + if (grab.window != null) { + if (!grab.owner_events) + return grab.window; + if (id == 0) + return grab.window; + } + return id; +} + function on_mouse_move (ev) { var id = get_surface_id(ev); + id = getEffectiveEventTarget (id); var pos = getPositionsFromEvent(ev, id); send_input ("m", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp]); } function on_mouse_over (ev) { var id = get_surface_id(ev); + real_window_with_mouse = id; + id = getEffectiveEventTarget (id); var pos = getPositionsFromEvent(ev, id); window_with_mouse = id; if (window_with_mouse != 0) { - send_input ("e", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp]); + send_input ("e", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, GDK_CROSSING_NORMAL]); } } function on_mouse_out (ev) { var id = get_surface_id(ev); + var origId = id; + id = getEffectiveEventTarget (id); var pos = getPositionsFromEvent(ev, id); if (id != 0) { - send_input ("l", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp]); + send_input ("l", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, GDK_CROSSING_NORMAL]); } + real_window_with_mouse = 0; window_with_mouse = 0; } +function doGrab(id, owner_events, time, implicit) { + var pos; + + if (window_with_mouse != id) { + if (window_with_mouse != 0) { + pos = getPositionsFromAbsCoord(last_x, last_y, window_with_mouse); + send_input ("l", [window_with_mouse, pos.root_x, pos.root_y, pos.win_x, pos.win_y, time, GDK_CROSSING_GRAB]); + } + pos = getPositionsFromAbsCoord(last_x, last_y, id); + send_input ("e", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, time, GDK_CROSSING_GRAB]); + window_with_mouse = id; + } + + grab.window = id; + grab.owner_events = owner_events; + grab.time = time; + grab.implicit = implicit; +} + +function doUngrab() { + var pos; + if (real_window_with_mouse != window_with_mouse) { + if (window_with_mouse != 0) { + pos = getPositionsFromAbsCoord(last_x, last_y, window_with_mouse); + send_input ("l", [window_with_mouse, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, GDK_CROSSING_UNGRAB]); + } + if (real_window_with_mouse != 0) { + pos = getPositionsFromAbsCoord(last_x, last_y, id); + send_input ("e", [real_window_with_mouse, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, GDK_CROSSING_UNGRAB]); + } + window_with_mouse = real_window_with_mouse; + } + grab.window = null; +} + function on_mouse_down (ev) { var id = get_surface_id(ev); + id = getEffectiveEventTarget (id); var pos = getPositionsFromEvent(ev, id); + if (grab.window != null) + doGrab (id, false, ev.timeStamp, true); send_input ("b", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, ev.button]); } function on_mouse_up (ev) { var id = get_surface_id(ev); + id = getEffectiveEventTarget (id); var pos = getPositionsFromEvent(ev, id); send_input ("B", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, ev.button]); + + if (grab.window != null && grab.implicit) + doUngrab(); } var last_key_down = 0; diff --git a/gdk/broadway/gdkdevice-broadway.c b/gdk/broadway/gdkdevice-broadway.c index e0a4dad44b..6b2b672f3e 100644 --- a/gdk/broadway/gdkdevice-broadway.c +++ b/gdk/broadway/gdkdevice-broadway.c @@ -182,7 +182,7 @@ gdk_broadway_device_query_state (GdkDevice *device, serial = broadway_output_query_pointer (broadway_display->output, impl->id); - reply = _gdk_broadway_display_block_for_input (display, 'q', serial); + reply = _gdk_broadway_display_block_for_input (display, 'q', serial, TRUE); if (reply != NULL) { @@ -266,13 +266,93 @@ gdk_broadway_device_grab (GdkDevice *device, GdkCursor *cursor, guint32 time_) { - return GDK_GRAB_NOT_VIEWABLE; + GdkDisplay *display; + GdkBroadwayDisplay *broadway_display; + GdkWindowImplBroadway *impl; + guint32 serial; + char *reply; + + display = gdk_device_get_display (device); + broadway_display = GDK_BROADWAY_DISPLAY (display); + + if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) + { + /* Device is a keyboard */ + return GDK_GRAB_SUCCESS; + } + else + { + /* Device is a pointer */ + + if (broadway_display->output) + { + impl = GDK_WINDOW_IMPL_BROADWAY (window->impl); + + serial = broadway_output_grab_pointer (broadway_display->output, + impl->id, owner_events, time_); + reply = _gdk_broadway_display_block_for_input (display, 'g', serial, FALSE); + if (reply != NULL) + { + char *p; + char cmd; + guint32 reply_serial; + int res; + + p = reply; + + cmd = *p++; + reply_serial = (guint32)strtol(p, &p, 10); + p++; /* Skip , */ + + res = strtol(p, &p, 10); + + return res; + } + } + + return GDK_GRAB_NOT_VIEWABLE; + } } +#define TIME_IS_LATER(time1, time2) \ + ( (( time1 > time2 ) && ( time1 - time2 < ((guint32)-1)/2 )) || \ + (( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 )) \ + ) + static void gdk_broadway_device_ungrab (GdkDevice *device, guint32 time_) { + GdkDisplay *display; + GdkBroadwayDisplay *broadway_display; + GdkDeviceGrabInfo *grab; + guint32 serial; + + display = gdk_device_get_display (device); + broadway_display = GDK_BROADWAY_DISPLAY (display); + + if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) + { + /* Device is a keyboard */ + } + else + { + /* Device is a pointer */ + + if (broadway_display->output) + { + serial = broadway_output_ungrab_pointer (broadway_display->output, time_); + + gdk_display_flush (display); + + grab = _gdk_display_get_last_device_grab (display, device); + if (grab && + (time_ == GDK_CURRENT_TIME || + grab->time == GDK_CURRENT_TIME || + !TIME_IS_LATER (grab->time, time_))) + grab->serial_end = serial; + } + } } static GdkWindow * diff --git a/gdk/broadway/gdkdisplay-broadway.c b/gdk/broadway/gdkdisplay-broadway.c index 9f90cc980e..f0e8dc1833 100644 --- a/gdk/broadway/gdkdisplay-broadway.c +++ b/gdk/broadway/gdkdisplay-broadway.c @@ -261,7 +261,8 @@ process_input_idle_cb (GdkBroadwayDisplay *display) /* Note: This may be called while handling a message (i.e. sorta recursively) */ char * -_gdk_broadway_display_block_for_input (GdkDisplay *display, char op, guint32 serial) +_gdk_broadway_display_block_for_input (GdkDisplay *display, char op, + guint32 serial, gboolean remove_message) { GdkBroadwayDisplay *broadway_display; char *message; @@ -295,8 +296,9 @@ _gdk_broadway_display_block_for_input (GdkDisplay *display, char op, guint32 ser msg_serial = (guint32)strtol(message+1, NULL, 10); if (msg_serial == serial) { - broadway_display->input_messages = - g_list_delete_link (broadway_display->input_messages, l); + if (remove_message) + broadway_display->input_messages = + g_list_delete_link (broadway_display->input_messages, l); return message; } } diff --git a/gdk/broadway/gdkeventsource.c b/gdk/broadway/gdkeventsource.c index 08ad05b7b5..26e23c7441 100644 --- a/gdk/broadway/gdkeventsource.c +++ b/gdk/broadway/gdkeventsource.c @@ -124,7 +124,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display, GdkScreen *screen; GdkWindow *root, *window; char *p; - int button, dir,key; + int button, dir, key, detail; guint32 serial; guint64 time; GdkEvent *event = NULL; @@ -142,6 +142,8 @@ _gdk_broadway_events_got_input (GdkDisplay *display, switch (cmd) { case 'e': /* Enter */ p = parse_pointer_data (p, &data); + p++; /* Skip , */ + detail = strtol(p, &p, 10); display_broadway->last_x = data.root_x; display_broadway->last_y = data.root_y; @@ -160,7 +162,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display, event->crossing.y = data.win_y; event->crossing.x_root = data.root_x; event->crossing.y_root = data.root_y; - event->crossing.mode = GDK_CROSSING_NORMAL; + event->crossing.mode = detail; event->crossing.detail = GDK_NOTIFY_ANCESTOR; gdk_event_set_device (event, display->core_pointer); @@ -178,6 +180,8 @@ _gdk_broadway_events_got_input (GdkDisplay *display, break; case 'l': /* Leave */ p = parse_pointer_data (p, &data); + p++; /* Skip , */ + detail = strtol(p, &p, 10); display_broadway->last_x = data.root_x; display_broadway->last_y = data.root_y; @@ -196,7 +200,7 @@ _gdk_broadway_events_got_input (GdkDisplay *display, event->crossing.x_root = data.root_x; event->crossing.y_root = data.root_y; event->crossing.mode = GDK_CROSSING_NORMAL; - event->crossing.detail = GDK_NOTIFY_ANCESTOR; + event->crossing.detail = detail; gdk_event_set_device (event, display->core_pointer); node = _gdk_event_queue_append (display, event); @@ -310,6 +314,10 @@ _gdk_broadway_events_got_input (GdkDisplay *display, } break; + case 'g': + case 'u': + _gdk_display_device_grab_update (display, display->core_pointer, NULL, serial); + break; case 'q': g_printerr ("Got unexpected query pointer reply w serial %d\n", serial); break; diff --git a/gdk/broadway/gdkprivate-broadway.h b/gdk/broadway/gdkprivate-broadway.h index 5e60e895a0..a442439a69 100644 --- a/gdk/broadway/gdkprivate-broadway.h +++ b/gdk/broadway/gdkprivate-broadway.h @@ -189,7 +189,8 @@ gchar *_gdk_broadway_display_utf8_to_string_target (GdkDisplay *display, GdkKeymap* _gdk_broadway_display_get_keymap (GdkDisplay *display); char * _gdk_broadway_display_block_for_input (GdkDisplay *display, char op, - guint32 serial); + guint32 serial, + gboolean remove); /* Window methods - testing */ void _gdk_broadway_window_sync_rendering (GdkWindow *window); -- 2.30.2